package zxs.sino.elite.hub.service.app.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

import com.github.yulichang.base.MPJBaseServiceImpl;
import org.springframework.transaction.annotation.Transactional;
import zxs.sino.elite.hub.model.vo.InterestVo;

import zxs.sino.elite.hub.mapper.app.InterestMapper;
import zxs.sino.elite.hub.model.app.InterestEntity;
import zxs.sino.elite.hub.service.app.InterestService;
import zxs.sino.elite.hub.utils.RedisKeys;
import zxs.sino.elite.hub.utils.RedisUtil;


@Service("interestService")
@RequiredArgsConstructor
public class InterestServiceImpl extends MPJBaseServiceImpl<InterestMapper, InterestEntity> implements InterestService {

    private final RedisUtil redisUtil;

    /**
     * 以树形显示所有的兴趣
     *
     */
    @Override
    public List<InterestVo> listWithTree() {

        // 1. 加入缓存逻辑，先从缓存中查询兴趣列表
        String interestsJson = (String) redisUtil.get(RedisKeys.getInterestKey());
        ObjectMapper objectMapper = new ObjectMapper();
        if (StringUtils.isEmpty(interestsJson)){
            List<InterestVo> interests = getInterests4Db();
            try {
                interestsJson = objectMapper.writeValueAsString(interests);
            } catch (Exception e) {
                throw new RuntimeException("转换兴趣列表为JSON字符串失败", e);
            }
            redisUtil.set(RedisKeys.getInterestKey(),interestsJson, -1);
        }
        try {
            return objectMapper.readValue(interestsJson, objectMapper.getTypeFactory().constructCollectionType(List.class, InterestVo.class));
        } catch (Exception e) {
            throw new RuntimeException("解析兴趣列表JSON数据失败", e);
        }
    }

    /**
     * 更新树形兴趣
     *
     * @param interest
     */
    @Override
    @Transactional
    public void updateWithTree(InterestEntity interest) {
        // 1.更新数据库数据
        this.saveOrUpdate(interest);

        // 2.删除redis缓存数据
        redisUtil.delete(RedisKeys.getInterestKey());

    }

    /**
     * 删除兴趣数据
     *
     * @param list
     */
    @Override
    @Transactional
    public void removeInterest(List<Long> list) {
        // 1.删除数据库数据
        this.removeByIds(list);

        // 2.删除redis缓存数据
        redisUtil.delete(RedisKeys.getInterestKey());
    }

    /**
     * 从数据中检索兴趣列表
     *
     */
    private List<InterestVo> getInterests4Db() {
        // 1.查询所有的兴趣数据
        List<InterestEntity> interests = this.list();

        /*
         * 2.递归组装2层兴趣的树形结构
         *   2.1 先过滤出一级分类
         *   2.2 针对一级分类再次递归组装一级分类的子分类，排序
         */
        return  interests.stream()
                .filter(interestEntity -> interestEntity.getParentId() == 0)
                .map(menu -> {
                    InterestVo interestVo = new InterestVo();
                    interestVo.setName(menu.getInterestName());
                    BeanUtils.copyProperties(menu, interestVo);
                    interestVo.setChildren(getChildren(interestVo, interests));
                    return interestVo;
                })
                .sorted(Comparator.comparingInt(menu -> (menu.getSort()  ==  null ? 0 : menu.getSort())))
                .collect(Collectors.toList());
    }

    /**
     * 递归查询当前兴趣的子兴趣
     * @param root 父兴趣
     * @param interests  所有兴趣
     */
    private List<InterestVo> getChildren(InterestVo root, List<InterestEntity> interests) {
        return interests.stream()
                .filter(interestEntity -> interestEntity.getParentId().equals(root.getInterestId()))
                .map(menu -> {
                    InterestVo interestVo = new InterestVo();
                    interestVo.setName(menu.getInterestName());
                    BeanUtils.copyProperties(menu, interestVo);
                    return interestVo;
                })
                .sorted(Comparator.comparingInt(menu -> (menu.getSort()  ==  null ? 0 : menu.getSort())))
                .collect(Collectors.toList());
    }
}